﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics.Contracts;

namespace SimpleIR
{
	/// <summary>
	/// Class to represent primitive value types (ints, floats, bools, chars, etc.).
	/// </summary>
	public class IRPrimitiveType : IRValueType
	{
		protected IRPrimitiveType(string tnamespace, string tname, TypeQualifiers tqual)
			: base(tnamespace, tname, tqual)
		{ ;}

		public override void ProcessBuiltinInfoAsNeeded()
		{ ;}

		[Pure]
		public static bool IsT1AssignableToT2(IRPrimitiveType pt1, IRPrimitiveType pt2)
		{
			IRPrimitiveType t1 = (pt1 is IREnumType) ? ((IREnumType)pt1).UnerlyingNumericType : pt1;
			IRPrimitiveType t2 = (pt2 is IREnumType) ? ((IREnumType)pt2).UnerlyingNumericType : pt2;

			if(t1 == t2)
				return true;

			if(t2 is IRSignedIntegerType & t1 is IRSignedIntegerType)
				return IRSignedIntegerType.IsT1AssignableToT2((IRSignedIntegerType)t1, (IRSignedIntegerType)t2);
			else if(t2 is IRUnSignedIntegerType & t1 is IRUnSignedIntegerType)
				return IRUnSignedIntegerType.IsT1AssignableToT2((IRUnSignedIntegerType)t1, (IRUnSignedIntegerType)t2);
			else if(t2 is IRFloatingPointType & t1 is IRFloatingPointType)
				return IRFloatingPointType.IsT1AssignableToT2((IRFloatingPointType)t1, (IRFloatingPointType)t2);
			if(t2 is IRNumericType | t1 is IRNumericType)
				return true;
			else
				return false;
		}
	}

	/// <summary>
	/// A simple class to represent all integer types.
	/// </summary>
	public class IRVoidType : IRPrimitiveType
	{
		public IRVoidType()
			: base("System", "Void", TypeQualifiers.PublicTag | TypeQualifiers.BuiltinTag)
		{ ;}
	}

	/// <summary>
	/// A simple class to represent all numeric types.
	/// </summary>
	public class IRNumericType : IRPrimitiveType
	{
		public IRNumericType(string iname)
			: base("System", iname, TypeQualifiers.PublicTag | TypeQualifiers.BuiltinTag)
		{ ;}
	}

	/// <summary>
	/// A simple class to represent all signed integer types.
	/// </summary>
	public sealed class IRSignedIntegerType : IRNumericType
	{
		public enum SignedTypeTag { StateToken, BoolTag, SByteTag, Int16Tag, Int32Tag, Int64Tag };

		public static String[] PrimNames = new String[] { "StateToken", "Boolean", "SByte", "Int16", "Int32", "Int64" };

		private readonly SignedTypeTag m_typetag;

		public IRSignedIntegerType(String tname)
			: base(tname)
		{
			if(tname.Equals("StateToken"))
				this.m_typetag = SignedTypeTag.StateToken;
			else if(tname.Equals("Boolean"))
				this.m_typetag = SignedTypeTag.BoolTag;
			else if(tname.Equals("SByte"))
				this.m_typetag = SignedTypeTag.SByteTag;
			else if(tname.Equals("Int16"))
				this.m_typetag = SignedTypeTag.Int16Tag;
			else if(tname.Equals("Int32"))
				this.m_typetag = SignedTypeTag.Int32Tag;
			else if(tname.Equals("Int64"))
				this.m_typetag = SignedTypeTag.Int64Tag;
			else
				throw new ArgumentException("Bad Prmitive Type Name: " + tname);
		}

		/// <summary>
		/// Use for identifying the exact type of the signed integer.
		/// </summary>
		public SignedTypeTag SignedTag
		{ get { return this.m_typetag; } }

		[Pure]
		public static bool IsT1AssignableToT2(IRSignedIntegerType pt1, IRSignedIntegerType pt2)
		{ return pt2.m_typetag - pt1.m_typetag >= 0; }
	}

	/// <summary>
	/// A simple class to represent all unsigned integer types.
	/// </summary>
	public sealed class IRUnSignedIntegerType : IRNumericType
	{
		public enum UnsignedTypeTag { CharTag, ByteTag, UInt16Tag, UInt32Tag, UInt64Tag };

		public static String[] PrimNames = new String[] { "Byte", "Char", "UInt16", "UInt32", "UInt64" };

		private readonly UnsignedTypeTag m_typetag;

		public IRUnSignedIntegerType(String tname)
			: base(tname)
		{
			if(tname.Equals("Char"))
				this.m_typetag = UnsignedTypeTag.CharTag;
			else if(tname.Equals("Byte"))
				this.m_typetag = UnsignedTypeTag.ByteTag;
			else if(tname.Equals("UInt16"))
				this.m_typetag = UnsignedTypeTag.UInt16Tag;
			else if(tname.Equals("UInt32"))
				this.m_typetag = UnsignedTypeTag.UInt32Tag;
			else if(tname.Equals("UInt64"))
				this.m_typetag = UnsignedTypeTag.UInt64Tag;
			else
				throw new ArgumentException("Bad Prmitive Type Name: " + tname);
		}

		/// <summary>
		/// Use for identifying the exact type of the unsigned integer.
		/// </summary>
		public UnsignedTypeTag UnSignedTag
		{ get { return this.m_typetag; } }

		[Pure]
		public static bool IsT1AssignableToT2(IRUnSignedIntegerType pt1, IRUnSignedIntegerType pt2)
		{
			if(pt2.m_typetag - pt1.m_typetag >= 0)
				return true;

			if(pt1.m_typetag == UnsignedTypeTag.CharTag)
				return (pt2.m_typetag == UnsignedTypeTag.CharTag) | (pt2.m_typetag == UnsignedTypeTag.ByteTag);
			else if(pt1.m_typetag == UnsignedTypeTag.ByteTag)
				return (pt2.m_typetag == UnsignedTypeTag.CharTag) | (pt2.m_typetag == UnsignedTypeTag.ByteTag);
			else
				return false;
		}
	}

	/// <summary>
	/// A simple class to represent all floating point types.
	/// </summary>
	public sealed class IRFloatingPointType : IRNumericType
	{
		public enum FloatingPointTypeTag { SingleTag, DoubleTag };

		public static String[] PrimNames = new String[] { "Single", "Double" };

		private readonly FloatingPointTypeTag m_typetag;

		public IRFloatingPointType(String tname)
			: base(tname)
		{
			if(tname.Equals("Single"))
				this.m_typetag = FloatingPointTypeTag.SingleTag;
			else if(tname.Equals("Double"))
				this.m_typetag = FloatingPointTypeTag.DoubleTag;
			else
				throw new ArgumentException("Bad Prmitive Type Name: " + tname);
		}

		/// <summary>
		/// Use for identifying the exact type of the floating point type.
		/// </summary>
		public FloatingPointTypeTag FloatingPointTag
		{ get { return this.m_typetag; } }

		[Pure]
		public static bool IsT1AssignableToT2(IRFloatingPointType pt1, IRFloatingPointType pt2)
		{ return pt2.m_typetag - pt1.m_typetag >= 0; }
	}

	/// <summary>
	/// A simple class to represent all function pointer types.
	/// </summary>
	public sealed class IRFunctionPointerType : IRPrimitiveType
	{
		public IRFunctionPointerType(string tnamespace, string tbasename, TypeQualifiers tqual)
			: base(tnamespace, tbasename, tqual)
		{
			Contract.Requires(!String.IsNullOrEmpty(tbasename));
		}
	}

	/// <summary>
	/// A simple class to represent all enumerated types.
	/// </summary>
	public sealed class IREnumType : IRPrimitiveType
	{
		private readonly IRNumericType m_underlyingtype;

		public IREnumType(string tnamespace, string tbasename, TypeQualifiers tqual, IRNumericType underlyingtype)
			: base(tnamespace, tbasename, tqual)
		{
			Contract.Requires(!String.IsNullOrEmpty(tbasename));

			this.m_underlyingtype = underlyingtype;
		}

		/// <summary>
		/// Return the underlying primitive type that backs this enumeration.
		/// </summary>
		public IRNumericType UnerlyingNumericType
		{ get { return this.m_underlyingtype; } }
	}
}
